package com.jintian.smart.kernel.flowable.controller;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jintian.smart.kernel.core.domain.PageQuery;
import com.jintian.smart.kernel.core.web.RestResultWrapper;
import com.jintian.smart.kernel.core.web.Result;
import com.jintian.smart.kernel.flowable.domain.entity.ActDeModel;
import com.jintian.smart.kernel.flowable.domain.entity.BizFlow;
import com.jintian.smart.kernel.flowable.domain.entity.FlowAct;
import com.jintian.smart.kernel.flowable.domain.query.BizFlowQuery;
import com.jintian.smart.kernel.flowable.domain.view.FlowDetailVo;
import com.jintian.smart.kernel.flowable.service.BizFlowService;
import com.jintian.smart.kernel.flowable.service.FlowActService;
import com.jintian.smart.kernel.flowable.service.ModelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.apache.commons.io.IOUtils;
import org.beetl.sql.core.page.PageResult;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.util.Iterator;

@RestResultWrapper
@RestController
@RequestMapping(path = "/flow/config")
@Schema(description = "流程配置")
public class FlowableController {
    @Resource
    private RepositoryService repositoryService;

    @Resource
    protected ModelService modelService;

    @Resource
    protected BizFlowService bizFlowService;

    @Resource
    protected FlowActService flowActService;

    /**
     * 查询全部流程数据
     *
     * @param request
     * @return
     */
    @PostMapping("/simpleQuery")
    public PageResult<BizFlow> simpleQuery(@RequestBody PageQuery<BizFlow, BizFlowQuery> request) {
        return bizFlowService.simpleQuery(request);
    }

    /**
     * 查询全部模型数据
     *
     * @return
     */
    @PostMapping("/getActDeModels")
    public Result<?> getActDeModels() {
        return Result.data(modelService.getActDeModels());
    }

    /**
     * @param modelId
     * @return
     * @throws IOException
     * @throws SQLException
     */
    @GetMapping("/model")
    public Result<?> getModel(String modelId) throws IOException, SQLException {
        FlowDetailVo detailVo = new FlowDetailVo();

        ActDeModel deModel = modelService.getActDeModelByModelId(modelId);
        if (checkStr(deModel.getModelEditorJson())) {
            // 将字符串解析为JSONObject对象
            JsonNode json = strJsonToJsonNode(deModel.getModelEditorJson());
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(json);// 转model
            detailVo.setBpmnXml(ProcessController.getBpmnXmlStr(model));
            return Result.data(detailVo);
        } else {
            return Result.fail(deModel.getModelEditorJson());
        }
    }

    /**
     * @param id
     * @return
     */
    @GetMapping("/bizFlow")
    public Result<?> getBizFlowById(String id) {
        BizFlow bizFlow = new BizFlow();
        bizFlow = bizFlowService.getById(id);
        if (bizFlow != null) {
            return Result.data(bizFlow);
        } else {
            return Result.fail();
        }
    }

    /**
     * 保存流程配置信息
     */
    @PostMapping("/saveBizFlow")
    public Result<?> saveBizFlow(@Valid @RequestBody BizFlow bizFlow) {
        return Result.condition(bizFlowService.saveOrUpdate(bizFlow));
    }

    /**
     * 获取流程节点信息
     *
     * @param id
     * @return
     */
    @GetMapping("/flowAct")
    public Result<?> getFlowActById(String id) {
        FlowAct flowAct = new FlowAct();
        flowAct = flowActService.getById(id);
        if (flowAct != null) {
            return Result.data(flowAct);
        } else {
            return Result.fail();
        }
    }

    /**
     * 保存流程节点信息
     */
    @PostMapping("/saveFlowAct")
    public Result<?> saveFlowAct(@Valid @RequestBody FlowAct flowAct) {
        return Result.condition(flowActService.saveOrUpdate(flowAct));
    }

    /**
     * json数据转换
     */
    public static JsonNode strJsonToJsonNode(String json) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            // JSON ----> JsonNode
            JsonNode rootNode = mapper.readTree(json);
            Iterator<String> keys = rootNode.fieldNames();
            while (keys.hasNext()) {
                String fieldName = keys.next();
                System.out.println(fieldName + ": " + rootNode.path(fieldName).toString());
            }
            return rootNode;
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 验证模型数据是否乱码
     */
    private static boolean checkStr(String str) {
        if (str != null) {
            if ((Charset.forName("GBK").newEncoder().canEncode(str))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 加载流程xml
     */
    @Operation(summary = "加载流程文件")
    @Parameters(value = {@Parameter(name = "file", in = ParameterIn.DEFAULT, description = "模板文件"),
            @Parameter(name = "bizId", in = ParameterIn.DEFAULT, description = "业务ID"),
            @Parameter(name = "flowBizId", in = ParameterIn.DEFAULT, description = "流程id")})
    @RequestMapping(value = "/loadFlowFile", method = RequestMethod.POST)
    public BizFlow loadFlowFileByInputStream(MultipartFile file, String bizId, String flowId) throws IOException, XMLStreamException {
        BizFlow bf = bizFlowService.create();
        if (null != flowId) {
            bf = bizFlowService.query().andEq(BizFlow::getId, flowId).unique();
        }

        byte[] bytes = IOUtils.toByteArray(file.getInputStream());

        if (file.getOriginalFilename().toLowerCase().endsWith(".xml")) {
            XMLInputFactory factory = XMLInputFactory.newInstance();
            XMLStreamReader reader = factory.createXMLStreamReader(new ByteArrayInputStream(bytes));
            Transformer transformer;
            try {
                transformer = TransformerFactory.newInstance().newTransformer();
                StringWriter stringWriter = new StringWriter();
                transformer.transform(new StAXSource(reader), new StreamResult(stringWriter));
                String res = stringWriter.toString();
                bf.setFlowModel(res);
                bf.setBizId(bizId);
                bizFlowService.saveOrUpdate(bf);
            } catch (TransformerFactoryConfigurationError | TransformerException e) {
                e.printStackTrace();
            }
        } else {
            throw new IllegalArgumentException("不支持的文件类型,只支持xml格式文件===");
        }
        return bf;
    }


    @Operation(summary = "发布流程")
    @Parameters(value = {@Parameter(name = "flowBizId", in = ParameterIn.DEFAULT, description = "流程id")})
    @RequestMapping(value = "/deployFlowByFlowBizId", method = RequestMethod.GET)
    public String deployFlowByFlowBizId(String flowId) throws XMLStreamException {
        String resultInfo = "发布失败！！！";
        BizFlow bf = bizFlowService.query().andEq(BizFlow::getId, flowId).unique();
        if (null != bf) {
            String fmodel = bf.getFlowModel();
            String fName = null == bf.getFlowName() ? bf.getFlowCode() : bf.getFlowName();
            if (null != fmodel) {
                XMLInputFactory factory = XMLInputFactory.newInstance();
                XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(fmodel));
                BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(reader);
                Deployment deploy = repositoryService.createDeployment().addBpmnModel(fName + ".bpmn20.xml", model).deploy();
                resultInfo = "发布成功！！！";
            }
        } else {
            throw new IllegalArgumentException("请上传流程模型，流程模型为空暂不支持发布！！！！");
        }
        return resultInfo;
    }
}
